xlv.obj.webObject={
	extend:function(){
		this.extend('xlv.obj.dm');
	},
	implement:function(){
	},
	create:function(tag){
		var div=document.createElement((tag||'div'));
		div.setAttribute('xlvobject','xlv.obj.webObject');
		return div;			
	},
	properties:{
		name:null,
		notifyRequest:false,
		webObjectProperties:null,
		returnData:'replace',//replace extend overwrite remove append
		returnDataAppendAt:null,
		currentNodeSustain:'equal',//position or equal
		propXPath:'properties'
	},
	method:{
		initial:function(){
			this.$super('xlv.obj.dm.initial');
			this.$V.currentNode=null;
			this.$V.traceNode=null;
			if(!this.$P.name) {
				this.$V.__started=true;
				this.$V.__responsed=true;
			}
		},
		start:function(){
			if(!this.$P.name)return;
			if(!this.$V.__started){
				if(this.$P.objectURL) this.queueTimeout((function(){this.request(null, "get-properties");}).bind(this));
				this.$V.__started=true;
			}else if(this.$V.__mvcArray){
				var mvc_array=this.$V.__mvcArray;
				this.$V.__mvcArray=null;
				this.$V.__queueNotifyMVC=true;
				var memo=this.createMemoObject({type:'create',methods:'start',from:this.$P.name,to:this.$P.objectURL,cancelable:false});
				this.notifyMVC('beforeModelChanged','setChanged',memo,mvc_array);
				this.notifyMVC('modelChanged','setChanged',memo,mvc_array);
				this.notifyMVC('afterModelChanged','setChanged',memo,mvc_array);
				this.$V.__queueNotifyMVC=false;
			}
		},
		dispose:function(){
			this.$super('xlv.obj.dm.dispose');
		},
		nodeIndexes:function(node){
			var root_node=((arguments.length>1&&Object.isElement(arguments[1]))?arguments[1]:this.getNode(this.$P.propXPath));
			return this.xml_nodeIndexes(node,root_node);
		},
		getNode:function(xpath){
			var root_node;
			if(xpath.startsWith('./')){
				root_node=this.$V.currentNode;
			}else{
				root_node=((arguments.length>1&&Object.isElement(arguments[1]))?arguments[1]:null);
				if(!root_node && !xpath.startsWith(this.$P.propXPath)) xpath=this.$P.propXPath+'/'+xpath;
				xpath=xpath.replace(/\./g,'/');
			}
			return this.xml_selectSingleNode(xpath,root_node);
		},
		getValue:function(xpath){
			var node=(xpath==null||arguments.length==0||xpath=='')?this.$V.currentNode:
					this.getNode(xpath,((arguments.length>1&&Object.isElement(arguments[1]))?arguments[1]:null));
			return node ? this.xml_getNodeValue(node) : '';
		},
		setValue:function(value,xpath){
			var node = (xpath==null||arguments.length==0||xpath=='')?this.$V.currentNode:
				this.getNode(xpath,((arguments.length>2&&Object.isElement(arguments[2]))?arguments[2]:null));
			if(node) {
				var old=this.xml_getNodeValue(node);
				if(old!=value){
					if(node.nodeType==1)node.setAttribute('xlv_upd','1');
					this.xml_setNodeValue(node,value);
					this.$V.updated=true;
					this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'update',methods:'setValue',src:node,from:old,to:value}));
				}
			}
		},
		getText:function(xpath){
			var node=(xpath==null||arguments.length==0||xpath=='')?this.$V.currentNode:
					this.getNode(xpath,((arguments.length>1&&Object.isElement(arguments[1]))?arguments[1]:null));
			return node ? this.xml_getNodeText(node) : '';
		},
		setText:function(text,xpath){
			var node = (xpath==null||arguments.length==0||xpath=='')?this.$V.currentNode:
				this.getNode(xpath,((arguments.length>2&&Object.isElement(arguments[2]))?arguments[2]:null));
			if(node) {
				var old=this.xml_getNodeText(node);
				if(old!=text){
					this.xml_setNodeText(node,text);
					this.$V.updated=true;
					this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'update',methods:'setText',src:node,from:old,to:text}));
				}
			}
		},
		select:function(node){
			if(!node&&!this.$V.currentNode)return;
			var memo=this.createMemoObject({type:'cursorMove',methods:'select',src:this.$V.currentNode,from:this.$V.currentNode,to:node});
			this.notifyMVC('beforeModelChanged','cursorMove',memo);
			if(memo.cancel)return false;
			if(node != this.$V.currentNode){
				this.$V.traceNode=this.$V.currentNode;
				this.$V.currentNode=node;
			}
			memo.cancelable=false;
			memo.src=memo.to=this.$V.currentNode;
			memo.from=this.$V.traceNode;
			this.notifyMVC('modelChanged','cursorMove',memo);
			this.notifyMVC('afterModelChanged','cursorMove',memo);
			return true;
		},
		remove:function(xpath){
			var node=(Object.isElement(xpath))?xpath:this.getNode(xpath,((arguments.length>1&&Object.isElement(arguments[1]))?arguments[1]:this.getNode(this.$P.propXPath)));
			if(node){
				var next=node;
				if(node==this.$V.currentNode){
					next=this.xml_selectSingleNode('following-sibling::*',node);
					if(!next) next=this.xml_selectSingleNode('preceding-sibling::*',node);
					if(!next) next=this.xml_selectSingleNode('parent::*',node);
				}
				this.notifyMVC('beforeModelChanged','nodeChanged',this.createMemoObject({type:'remove',methods:'remove',src:node,from:node,to:next}));
				this.$V.updated=true;
				node.parentNode.removeChild(node);
				this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'remove',methods:'remove',src:node,from:node,to:next}));
				this.notifyMVC('afterModelChanged','nodeChanged',this.createMemoObject({type:'remove',methods:'remove',src:node,from:node,to:next}));
				if(node==this.$V.currentNode) this.select(next);
			}
			return node;
		},
		insert:function(tag_names,value){
			var root_node=((arguments.length>2&&Object.isElement(arguments[2]))?arguments[2]:this.getNode(this.$P.propXPath));
			var elements=xml_createElements(tag_names,value);
			var source=this.xml_getDocument().createElement('source');
			source.appendChild(elements[0]);
			xlv.util.xmlExtendNodes(root_node,source,'append',false);
			this.$V.updated=true;
			var last=elements[elements.length-1];
			this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'insert',methods:'insert',src:last,from:elements[0],to:last}));
			return last;
		},
		replace:function(new_node,xpath){
			var old_node=(Object.isElement(xpath))?xpath:this.getNode(xpath,((arguments.length>2&&Object.isElement(arguments[2]))?arguments[2]:this.getNode(this.$P.propXPath)));
			if(old_node){
				this.$V.updated=true;
				old_node.parentNode.replaceChild(new_node,old_node);
				this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'update',methods:'replace',src:old_node,from:old_node,to:new_node}));
			}
		},
		substitute:function(new_node,xpath){
			var old_node=(Object.isElement(xpath))?xpath:this.getNode(xpath,((arguments.length>2&&Object.isElement(arguments[2]))?arguments[2]:this.getNode(this.$P.propXPath)));
			if(old_node){
				this.$V.updated=true;
				while(old_node.firstChild)new_node.appendChild(old_node.firstChild);
				old_node.parentNode.replaceChild(new_node,old_node);
				this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'update',methods:'substitute',src:old_node,from:old_node,to:new_node}));
			}
		},
		webMethod:function(method_name){
			var method=(method_name.startsWith(this.$P.propXPath))?method_name:this.$P.propXPath+'/'+method_name;
			var method_node=this.xml_selectSingleNode(method,null);
			if(method_node){
				var return_node=this.xml_selectSingleNode('return',method_node);
				var arguments_node;
				if(return_node){
					var tmp_return=this.xml_createElement('return');
					var parent=return_node.parentNode;
					parent.replaceChild(tmp_return,return_node);
					arguments_node=method_node.cloneNode(true);
					parent.replaceChild(return_node,tmp_return);
				}else arguments_node=method_node.cloneNode(true);
				this.queueTimeout((function(){this.request(null,method_name,arguments_node);}).bind(this));
				return true;
			}
			return false;
		},
		setWebObjectProperties:function(json_prop){
			var p=this.$P,p_xml=this.xml_properties;
			if(p_xml.responseXML){
				var xmlstring='<properties>'+json2xml(json_prop)+'</properties>';
				var xml=this.xml_stringToNode(xmlstring);
				var prop=this.xml_selectSingleNode(this.$P.propXPath);
				if(prop&&xml)xlv.util.xmlExtendNodes(prop,xml,'overwrite',false);
			}else p.webObjectProperties=Object.extend(p.webObjectProperties||{},json_prop);
		},
		webObjectResponse:function(){
			var res_method_names=this.xml_responseMethodNames();
			if(res_method_names=='get-properties'){
				if(this.$P.objectURL && this.$P.methodName){
					this.queueTimeout((function(){if(!this.webMethod(this.$P.methodName))this._notifiyReady();}).bind(this));
				}else{
					var memo=this.createMemoObject({type:'create',methods:res_method_names,cancelable:false});
					this.notifyMVC('beforeModelChanged','setChanged',memo);
					this.notifyMVC('modelChanged','setChanged',memo);
					this.notifyMVC('afterModelChanged','setChanged',memo);
					this._notifiyReady();
				}
				return;
			}
			var clone_response=(arguments.length>0)?arguments[0]:false;
			var p_xml=this.xml_properties;
			if(p_xml.responseXML && (p_xml.responseXML!=p_xml.currentXML)){
				var memo=this.createMemoObject({type:'create',methods:res_method_names,cancelable:false});
				var current_indexes=null;
				if(this.$V.currentNode)	current_indexes=this.nodeIndexes(this.$V.currentNode);
				this.notifyMVC('beforeModelChanged','setChanged',memo);
				this.returnDataManage(clone_response);
				this.notifyMVC('modelChanged','setChanged',memo);
				var current_node;
				if(current_indexes)current_node=this.getNode(xlv.util.indexesToXPath(current_indexes));
				if(this.$P.currentNodeSustain=='position')this.$V.currentNode=current_node;
				else if(this.$V.currentNode!=current_node)this.$V.currentNode=null;
				this.notifyMVC('afterModelChanged','setChanged',memo);
				this.queueTimeout(function(){this.select(current_node);}.bind(this));
				this._notifiyReady();
			}
		},
		returnDataManage:function(clone_response){
			var p_xml=this.xml_properties;
			var prop=this.xml_selectSingleNode(this.$P.propXPath);
			if(this.$P.returnData=='replace'){
				var methods=this.xml_selectNodes('child::*',p_xml.responseXML.documentElement);
				for(var i=0;i<methods.length;++i){
					var return_node=this.xml_selectSingleNode(this.$P.propXPath+'/'+methods[i].nodeName+'/return');
					if(return_node)return_node.parentNode.removeChild(return_node);
				}
				xlv.util.xmlExtendNodes(prop,p_xml.responseXML.documentElement,'extend',clone_response);
			}else {
				if(this.$P.returnData=='append'){
					var at=this.xml_selectSingleNode(this.$P.returnDataAppendAt,prop);
					var from_nodes=(clone_response)?p_xml.responseXML.documentElement.cloneNode(true):p_xml.responseXML.documentElement;
					var from=this.xml_selectSingleNode(this.$P.returnDataAppendAt,from_nodes);
					from=this.xml_adoptNode(from);
					if(at && from){
						xlv.util.xmlExtendAttributes(at,from,'overwrite');
						while(from.firstChild)at.appendChild(from.firstChild);
					}
				}else{
					xlv.util.xmlExtendNodes(prop,p_xml.responseXML.documentElement,this.$P.returnData,clone_response);
				}
			}
		},
		response:function(){
			var p_xml=this.xml_properties;
			if(p_xml.responseXML && (p_xml.responseXML==p_xml.currentXML))this.setWebObjectProperties(this.$P.webObjectProperties);
			this.webObjectResponse();
			if(!p_xml.objectURL && p_xml.currentXML)this._notifiyReady();
		}
	}
};

